home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-19 / rcs55.zip / RCSEDIT.C < prev    next >
C/C++ Source or Header  |  1991-09-15  |  23KB  |  844 lines

  1. /*
  2.  *                     RCS stream editor
  3.  */
  4. /**********************************************************************************
  5.  *                       edits the input file according to a
  6.  *                       script from stdin, generated by diff -n
  7.  *                       performs keyword expansion
  8.  **********************************************************************************
  9.  */
  10.  
  11. /* Copyright (C) 1982, 1988, 1989 Walter Tichy
  12.    Copyright 1990 by Paul Eggert
  13.    Distributed under license by the Free Software Foundation, Inc.
  14.  
  15. This file is part of RCS.
  16.  
  17. RCS is free software; you can redistribute it and/or modify
  18. it under the terms of the GNU General Public License as published by
  19. the Free Software Foundation; either version 1, or (at your option)
  20. any later version.
  21.  
  22. RCS is distributed in the hope that it will be useful,
  23. but WITHOUT ANY WARRANTY; without even the implied warranty of
  24. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  25. GNU General Public License for more details.
  26.  
  27. You should have received a copy of the GNU General Public License
  28. along with RCS; see the file COPYING.  If not, write to
  29. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  30.  
  31. Report problems and direct all questions to:
  32.  
  33.     rcs-bugs@cs.purdue.edu
  34.  
  35. */
  36.  
  37.  
  38. /* $Log: rcsedit.c%v $
  39.  * Revision 1.2  1991/08/23  13:33:11  SGP
  40.  * Ported to MSDOS using Borland C++
  41.  *
  42.  * Revision 5.5  1990/12/30  05:07:35  eggert
  43.  * Fix report of busy RCS files when !defined(O_CREAT) | !defined(O_EXCL).
  44.  *
  45.  * Revision 5.4  1990/11/01  05:03:40  eggert
  46.  * Permit arbitrary data in comment leaders.
  47.  *
  48.  * Revision 5.3  1990/09/11  02:41:13  eggert
  49.  * Tune expandline().
  50.  *
  51.  * Revision 5.2  1990/09/04  08:02:21  eggert
  52.  * Count RCS lines better.  Improve incomplete line handling.
  53.  *
  54.  * Revision 5.1  1990/08/29  07:13:56  eggert
  55.  * Add -kkvl.
  56.  * Fix bug when getting revisions to files ending in incomplete lines.
  57.  * Fix bug in comment leader expansion.
  58.  *
  59.  * Revision 5.0  1990/08/22  08:12:47  eggert
  60.  * Don't require final newline.
  61.  * Don't append "checked in with -k by " to logs,
  62.  * so that checking in a program with -k doesn't change it.
  63.  * Don't generate trailing white space for empty comment leader.
  64.  * Remove compile-time limits; use malloc instead.  Add -k, -V.
  65.  * Permit dates past 1999/12/31.  Make lock and temp files faster and safer.
  66.  * Ansify and Posixate.  Check diff's output.
  67.  *
  68.  * Revision 4.8  89/05/01  15:12:35  narten
  69.  * changed copyright header to reflect current distribution rules
  70.  * 
  71.  * Revision 4.7  88/11/08  13:54:14  narten
  72.  * misplaced semicolon caused infinite loop
  73.  * 
  74.  * Revision 4.6  88/08/09  19:12:45  eggert
  75.  * Shrink stdio code size; allow cc -R.
  76.  * 
  77.  * Revision 4.5  87/12/18  11:38:46  narten
  78.  * Changes from the 43. version. Don't know the significance of the
  79.  * first change involving "rewind". Also, additional "lint" cleanup.
  80.  * (Guy Harris)
  81.  * 
  82.  * Revision 4.4  87/10/18  10:32:21  narten
  83.  * Updating version numbers. Changes relative to version 1.1 actually
  84.  * relative to 4.1
  85.  * 
  86.  * Revision 1.4  87/09/24  13:59:29  narten
  87.  * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  88.  * warnings)
  89.  * 
  90.  * Revision 1.3  87/09/15  16:39:39  shepler
  91.  * added an initializatin of the variables editline and linecorr
  92.  * this will be done each time a file is processed.
  93.  * (there was an obscure bug where if co was used to retrieve multiple files
  94.  *  it would dump)
  95.  * fix attributed to  Roy Morris @FileNet Corp ...!felix!roy
  96.  * 
  97.  * Revision 1.2  87/03/27  14:22:17  jenkins
  98.  * Port to suns
  99.  * 
  100.  * Revision 4.1  83/05/12  13:10:30  wft
  101.  * Added new markers Id and RCSfile; added locker to Header and Id.
  102.  * Overhauled expandline completely() (problem with $01234567890123456789@).
  103.  * Moved trymatch() and marker table to rcskeys.c.
  104.  * 
  105.  * Revision 3.7  83/05/12  13:04:39  wft
  106.  * Added retry to expandline to resume after failed match which ended in $.
  107.  * Fixed truncation problem for $19chars followed by@@.
  108.  * Log no longer expands full path of RCS file.
  109.  * 
  110.  * Revision 3.6  83/05/11  16:06:30  wft
  111.  * added retry to expandline to resume after failed match which ended in $.
  112.  * Fixed truncation problem for $19chars followed by@@.
  113.  * 
  114.  * Revision 3.5  82/12/04  13:20:56  wft
  115.  * Added expansion of keyword Locker.
  116.  *
  117.  * Revision 3.4  82/12/03  12:26:54  wft
  118.  * Added line number correction in case editing does not start at the
  119.  * beginning of the file.
  120.  * Changed keyword expansion to always print a space before closing KDELIM;
  121.  * Expansion for Header shortened.
  122.  *
  123.  * Revision 3.3  82/11/14  14:49:30  wft
  124.  * removed Suffix from keyword expansion. Replaced fclose with ffclose.
  125.  * keyreplace() gets log message from delta, not from curlogmsg.
  126.  * fixed expression overflow in while(c=putc(GETC....
  127.  * checked nil printing.
  128.  *
  129.  * Revision 3.2  82/10/18  21:13:39  wft
  130.  * I added checks for write errors during the co process, and renamed
  131.  * expandstring() to xpandstring().
  132.  *
  133.  * Revision 3.1  82/10/13  15:52:55  wft
  134.  * changed type of result of getc() from char to int.
  135.  * made keyword expansion loop in expandline() portable to machines
  136.  * without sign-extension.
  137.  */
  138.  
  139.  
  140. #include "rcsbase.h"
  141.  
  142. libId(editId, "$Id: rcsedit.c%v 1.2 1991/08/23 13:33:11 SGP Exp $")
  143.  
  144. static void keyreplace P((enum markers,const struct hshentry*,FILE*));
  145.  
  146.  
  147. FILE *fcopy;         /* result file descriptor                */
  148. const char *resultfile;  /* result file name                    */
  149. int locker_expansion;     /* should the locker name be appended to Id val?   */
  150. static FILE *fedit;     /* edit   file descriptor                */
  151. static const char *editfile;     /* edit file                    */
  152. static const char *editdir;  /* edit directory                    */
  153. static unsigned long editline; /*fedit line counter; is always #lines+1     */
  154. static long linecorr; /* #adds - #deletes in each edit run.            */
  155.                /*used to correct editline in case file is not rewound after */
  156.                /* applying one delta                                        */
  157.  
  158. #define DIRTEMPNAMES 3
  159. enum maker {notmade, real, effective};
  160. struct buf dirtfname[DIRTEMPNAMES];        /* unlink these when done */
  161. static volatile enum maker dirtfmaker[DIRTEMPNAMES];    /* if these are set */
  162.  
  163.     FILE *
  164. initeditfiles(const char *dir)
  165. /* Function: Initializes resultfile and editfile with temporary filenames
  166.  * in directory dir. Opens resultfile for reading and writing, with fcopy
  167.  * as file descriptor. fedit is set to nil.
  168.  */
  169. {
  170.     editline = 0;    /* make sure we start from the beginning*/
  171.     linecorr = 0;
  172.     editdir = dir;
  173.     resultfile = makedirtemp(dir,1);
  174.     editfile = nil;
  175.         fedit=nil;
  176.     errno = 0;
  177.     return fcopy = fopen(resultfile,"w+");
  178. }
  179.  
  180.     void
  181. inittmpeditfiles()
  182. {
  183.     if (!initeditfiles(tmp()))
  184.         efaterror(resultfile);
  185. }
  186.  
  187.  
  188.     void
  189. arewind(FILE *f)
  190. {
  191.     if (fseek(f, (long)0, SEEK_SET) == EOF)
  192.         IOerror();
  193. }
  194.  
  195.     void
  196. swapeditfiles(int tostdout)
  197. /* Function: swaps resultfile and editfile, assigns fedit=fcopy,
  198.  * rewinds fedit for reading, and opens resultfile for reading and
  199.  * writing, using fcopy. If tostdout, fcopy is set to stdout.
  200.  */
  201. {
  202.     const char *tmpptr;
  203.         fedit=fcopy;
  204.     arewind(fedit);
  205.         editline = 1; linecorr=0;
  206.         tmpptr=editfile; editfile=resultfile; resultfile=tmpptr;
  207.         if (tostdout)
  208.                 fcopy=stdout;
  209.     else {
  210.         if (!resultfile)
  211.         resultfile = makedirtemp(editdir,2);
  212.         errno = 0;
  213.         if (!(fcopy = fopen(resultfile,"w+")))
  214.         efaterror(resultfile);
  215.         }
  216. }
  217.  
  218.  
  219.     void
  220. finishedit(const struct hshentry *delta)
  221. /* copy the rest of the edit file and close it (if it exists).
  222.  * if delta!=nil, perform keyword substitution at the same time.
  223.  */
  224. {
  225.     register FILE *fe, *fc;
  226.  
  227.     fe = fedit;
  228.     if (fe) {
  229.         fc = fcopy;
  230.                 if (delta!=nil) {
  231.             while (0 < expandline(fe,fc,delta,false,(FILE*)NULL))
  232.                 ;
  233.                 } else {
  234.             fastcopy(fe,fc);
  235.                 }
  236.         ffclose(fe);
  237.         }
  238. }
  239.  
  240.  
  241.  
  242.     static exiting void
  243. editEndsPrematurely()
  244. {
  245.     fatserror("edit script ends prematurely");
  246. }
  247.  
  248.     static exiting void
  249. editLineNumberOverflow()
  250. {
  251.     fatserror("edit script refers to line past end of file");
  252. }
  253.  
  254.  
  255.     static void
  256. copylines(register unsigned long upto,const struct hshentry *delta)
  257. /* Function: copies input lines editline..upto-1 from fedit to fcopy.
  258.  * If delta != nil, keyword expansion is done simultaneously.
  259.  * editline is updated. Rewinds a file only if necessary.
  260.  */
  261. {
  262.     register int c;
  263.     register FILE *fe, *fc;
  264.  
  265.     if (upto < editline) {
  266.                 /* swap files */
  267.                 finishedit((struct hshentry *)nil); swapeditfiles(false);
  268.                 /* assumes edit only during last pass, from the beginning*/
  269.         }
  270.     fe = fedit;
  271.     fc = fcopy;
  272.     if (editline < upto)
  273.         if (delta)
  274.         do {
  275.             if (expandline(fe,fc,delta,false,(FILE*)NULL) <= 0)
  276.                 goto unexpected_EOF;
  277.         } while (++editline < upto);
  278.         else
  279.         do {
  280.             do {
  281.                 c = getc(fe);
  282.                 if (c == EOF)
  283.                     goto unexpected_EOF;
  284.                 aputc(c, fc);
  285.             } while (c != '\n');
  286.         } while (++editline < upto);
  287.     return;
  288.  
  289.     unexpected_EOF:
  290.     editLineNumberOverflow();
  291. }
  292.  
  293.  
  294.  
  295.     void
  296. xpandstring(const struct hshentry *delta)
  297. /* Function: Reads a string terminated by SDELIM from finptr and writes it
  298.  * to fcopy. Double SDELIM is replaced with single SDELIM.
  299.  * Keyword expansion is performed with data from delta.
  300.  * If foutptr is nonnull, the string is also copied unchanged to foutptr.
  301.  */
  302. {
  303.     while (0 < expandline(finptr,fcopy,delta,true,foutptr))
  304.         ;
  305. }
  306.  
  307.  
  308.     void
  309. copystring()
  310. /* Function: copies a string terminated with a single SDELIM from finptr to
  311.  * fcopy, replacing all double SDELIM with a single SDELIM.
  312.  * If foutptr is nonnull, the string also copied unchanged to foutptr.
  313.  * editline is set to (number of lines copied)+1.
  314.  * Assumption: next character read is first string character.
  315.  */
  316. {    register c;
  317.     register FILE *fin, *frew, *fcop;
  318.     register int amidline;
  319.  
  320.     fin=finptr; frew=foutptr; fcop=fcopy;
  321.         editline=1;
  322.     amidline = false;
  323.     for (;;) {
  324.         GETC(fin,frew,c);
  325.         switch (c) {
  326.             case EOF:
  327.             unterminatedString();
  328.             /*NOTREACHED*/
  329.             case '\n':
  330.             ++editline;
  331.             ++rcsline;
  332.             amidline = false;
  333.             break;
  334.             case SDELIM:
  335.             GETC(fin,frew,c);
  336.             if (c != SDELIM) {
  337.                 /* end of string */
  338.                 nextc = c;
  339.                 editline += amidline;
  340.                 return;
  341.             }
  342.             /* fall into */
  343.             default:
  344.             amidline = true;
  345.             break;
  346.                 }
  347.         aputc(c,fcop);
  348.         }
  349. }
  350.  
  351.  
  352.  
  353.  
  354.     void
  355. editstring(const struct hshentry *delta)
  356. /* Function: reads an edit script from finptr and applies it to
  357.  * file fedit; the result is written to fcopy.
  358.  * If delta!=nil, keyword expansion is performed simultaneously.
  359.  * If foutptr is set, the edit script is also copied verbatim to foutptr.
  360.  * Assumes that all these files are open.
  361.  * If running out of lines in fedit, fedit and fcopy are swapped.
  362.  * resultfile and editfile are the names of the files that go with fcopy
  363.  * and fedit, respectively.
  364.  * Assumes the next input character from finptr is the first character of
  365.  * the edit script. Resets nextc on exit.
  366.  */
  367. {
  368.         int ed; /* editor command */
  369.         register int c;
  370.     register FILE *fin, *frew, *f;
  371.     register unsigned long i;
  372.     unsigned long line_lim;
  373.     struct diffcmd dc;
  374.  
  375.         editline += linecorr; linecorr=0; /*correct line number*/
  376.     frew = foutptr;
  377.     fin = finptr;
  378.     line_lim = ULONG_MAX;
  379.     initdiffcmd(&dc);
  380.     while (0  <=  (ed = getdiffcmd(fin,SDELIM,frew,&dc)))
  381.         if (line_lim <= dc.line1)
  382.             editLineNumberOverflow();
  383.         else if (!ed) {
  384.             copylines(dc.line1, delta);
  385.                         /* skip over unwanted lines */
  386.             i = dc.nlines;
  387.             linecorr -= i;
  388.             editline += i;
  389.             f = fedit;
  390.             do {
  391.                                 /*skip next line*/
  392.                 while ((c=getc(f))!='\n')
  393.                     if (c==EOF) {
  394.                         if (i!=1)
  395.                         editLineNumberOverflow();
  396.                         line_lim = dc.dafter;
  397.                         break;
  398.                     }
  399.             } while (--i);
  400.         } else {
  401.             copylines(dc.line1+1, delta); /*copy only; no delete*/
  402.             i = dc.nlines;
  403.             linecorr += i;
  404.             f = fcopy;
  405.             do {
  406.                                 /*copy next line from script*/
  407.                                 if (delta!=nil)
  408.                     switch (expandline(fin,f,delta,true,frew)) {
  409.                     case 0:
  410.                         if (i==1)
  411.                         return;
  412.                         /* fall into */
  413.                     case -1:
  414.                         editEndsPrematurely();
  415.                     }
  416.                                 else {
  417.                        for (;;) {
  418.                         GETC(fin,frew,c);
  419.                         if (c == EOF)
  420.                         editEndsPrematurely();
  421.                         aputc(c, f);
  422.                         if (c == '\n')
  423.                         break;
  424.                         if (c==SDELIM) {
  425.                         GETC(fin,frew,c);
  426.                         if (c!=SDELIM) {
  427.                             if (--i)
  428.                             editEndsPrematurely();
  429.                             nextc = c;
  430.                             return;
  431.                         }
  432.                        }
  433.                        }
  434.                        ++rcsline;
  435.                 }
  436.             } while (--i);
  437.                 }
  438.     GETC(fin,frew,c);
  439.     nextc = c;
  440. }
  441.  
  442.  
  443.  
  444. /* The rest is for keyword expansion */
  445.  
  446.  
  447.  
  448.     int
  449. expandline(register FILE *in,
  450.            register FILE *out,
  451.            const struct hshentry *delta,
  452.            int delimstuffed,
  453.            register FILE *frew)
  454. /* Function: Reads a line from in and writes it to out.
  455.  * If DELIMSTUFFED is set, double SDELIM is replaced with single SDELIM.
  456.  * Keyword expansion is performed with data from delta.
  457.  * If FREW is set, the line is also copied unchanged to FREW.
  458.  * DELIMSTUFFED must be set if FREW is set.
  459.  * Yields -1 if no data is copied, 0 if an incomplete line is copied,
  460.  * 1 if a complete line is copied.
  461.  */
  462. {
  463.     register c;
  464.     register char * tp;
  465.     register int ds, r;
  466.     const char *tlim;
  467.     char keystring[keylength+2];
  468.     static struct buf keyval;
  469.         enum markers matchresult;
  470.  
  471.     ds = delimstuffed;
  472.     r = -1;
  473.     GETC(in,frew,c);
  474.         for (;;) {
  475.         switch (c) {
  476.             case EOF:
  477.                         if(ds) {
  478.                 unterminatedString();
  479.                         }
  480.             return r;
  481.  
  482.             case SDELIM:
  483.             if (ds) {
  484.                 GETC(in,frew,c);
  485.                 if (c != SDELIM) {
  486.                                 /* end of string */
  487.                                 nextc=c;
  488.                 return r;
  489.                 }
  490.             }
  491.             /* fall into */
  492.             default:
  493.             r = 0;
  494.             aputc(c,out);
  495.             break;
  496.  
  497.             case '\n':
  498.             rcsline += ds;
  499.             aputc(c,out);
  500.             return 1;
  501.  
  502.             case KDELIM:
  503.             r = 0;
  504.                         /* check for keyword */
  505.                         /* first, copy a long enough string into keystring */
  506.             tp=keystring;
  507.             for (;;) {
  508.                 GETC(in,frew,c);
  509.                 if (tp < keystring+keylength)
  510.                 switch (ctab[c]) {
  511.                     case LETTER: case Letter:
  512.                     *tp++ = c;
  513.                     continue;
  514.                     default:
  515.                     break;
  516.                 }
  517.                 break;
  518.                         }
  519.             *tp++ = c; *tp = '\0';
  520.             matchresult = trymatch(keystring);
  521.             if (matchresult==Nomatch) {
  522.                 tp[-1] = 0;
  523.                 aprintf(out, "%c%s", KDELIM, keystring);
  524.                 continue;   /* last c handled properly */
  525.             }
  526.  
  527.             /* Now we have a keyword terminated with a K/VDELIM */
  528.             if (c==VDELIM) {
  529.                   /* try to find closing KDELIM, and replace value */
  530.                   bufalloc(&keyval, 1);
  531.                   tp = keyval.string;
  532.                   tlim = tp + keyval.size;
  533.                   for (;;) {
  534.                       GETC(in,frew,c);
  535.                       if (c==EOF || c=='\n' || c==KDELIM)
  536.                     break;
  537.                       *tp++ =c;
  538.                       if (tlim <= tp)
  539.                       tp = bufenlarge(&keyval, &tlim);
  540.                       if (c==SDELIM && ds) { /*skip next SDELIM */
  541.                         GETC(in,frew,c);
  542.                         if (c != SDELIM) {
  543.                             /* end of string before closing KDELIM or newline */
  544.                             *tp = 0;
  545.                             aprintf(out, "%c%s%s", KDELIM, keystring, keyval.string);
  546.                             nextc = c;
  547.                             return 0;
  548.                         }
  549.                       }
  550.                   }
  551.                   if (c!=KDELIM) {
  552.                     /* couldn't find closing KDELIM -- give up */
  553.                     *tp = 0;
  554.                     aprintf(out, "%c%s%s", KDELIM, keystring, keyval.string);
  555.                     continue;   /* last c handled properly */
  556.                   }
  557.             }
  558.             /* now put out the new keyword value */
  559.             keyreplace(matchresult,delta,out);
  560.                 }
  561.         GETC(in,frew,c);
  562.         }
  563. }
  564.  
  565.  
  566. const char ciklog[ciklogsize] = "checked in with -k by ";
  567.  
  568.     static void
  569. keyreplace(enum markers marker,
  570.            const struct hshentry *delta,
  571.            register FILE *out)
  572. /* function: outputs the keyword value(s) corresponding to marker.
  573.  * Attributes are derived from delta.
  574.  */
  575. {
  576.     register const char *sp, *cp, *date;
  577.     register char c;
  578.     register size_t cs, cw, ls;
  579.     int RCSv;
  580.  
  581.     sp = Keyword[(int)marker];
  582.  
  583.     if (Expand == KEY_EXPAND) {
  584.         aprintf(out, "%c%s%c", KDELIM, sp, KDELIM);
  585.         return;
  586.     }
  587.  
  588.         date= delta->date;
  589.     RCSv = RCSversion;
  590.  
  591.     if (Expand == KEYVAL_EXPAND  ||  Expand == KEYVALLOCK_EXPAND)
  592.         aprintf(out, "%c%s%c%c", KDELIM, sp, VDELIM,
  593.             marker==Log && RCSv<VERSION(5)  ?  '\t'  :  ' '
  594.         );
  595.  
  596.         switch (marker) {
  597.         case Author:
  598.         aputs(delta->author, out);
  599.                 break;
  600.         case Date:
  601.         printdate(out, date, " ");
  602.                 break;
  603.         case Id:
  604.     case Header:
  605.         aprintf(out, "%s %s ",
  606.               marker==Id || RCSv<VERSION(4)
  607.             ? bindex(RCSfilename,SLASH)
  608.             : getfullRCSname(),
  609.             delta->num
  610.         );
  611.         printdate(out, date, " ");
  612.         aprintf(out, " %s %s",
  613.             delta->author,
  614.               RCSv==VERSION(3) && delta->lockedby ? "Locked"
  615.             : delta->state
  616.         );
  617.         if (delta->lockedby!=nil)
  618.             if (VERSION(5) <= RCSv) {
  619.             if (locker_expansion || Expand==KEYVALLOCK_EXPAND)
  620.                 aprintf(out, " %s", delta->lockedby);
  621.             } else if (RCSv == VERSION(4))
  622.             aprintf(out, " Locker: %s", delta->lockedby);
  623.                 break;
  624.         case Locker:
  625.         if (delta->lockedby)
  626.             if (
  627.                 locker_expansion
  628.             ||    Expand == KEYVALLOCK_EXPAND
  629.             ||    RCSv <= VERSION(4)
  630.             )
  631.             aputs(delta->lockedby, out);
  632.                 break;
  633.         case Log:
  634.         case RCSfile:
  635.         aputs(bindex(RCSfilename,SLASH), out);
  636.                 break;
  637.         case Revision:
  638.         aputs(delta->num, out);
  639.                 break;
  640.         case Source:
  641.         aputs(getfullRCSname(), out);
  642.                 break;
  643.         case State:
  644.         aputs(delta->state, out);
  645.                 break;
  646.     default:
  647.         break;
  648.         }
  649.     if (Expand == KEYVAL_EXPAND  ||  Expand == KEYVALLOCK_EXPAND) {
  650.         afputc(' ', out);
  651.         afputc(KDELIM, out);
  652.     }
  653.     if (marker == Log) {
  654.         sp = delta->log.string;
  655.         ls = delta->log.size;
  656.         if (sizeof(ciklog)-1<=ls && !strncmp(sp,ciklog,sizeof(ciklog)-1))
  657.             return;
  658.         afputc('\n', out);
  659.         cp = Comment.string;
  660.         cw = cs = Comment.size;
  661.         awrite(cp, cs, out);
  662.         aprintf(out, "Revision %s  ", delta->num);
  663.         printdate(out, date, "  ");
  664.         aprintf(out, "  %s", delta->author);
  665.         /* Do not include state: it may change and is not updated.  */
  666.         /* Comment is the comment leader.  */
  667.         if (VERSION(5) <= RCSv)
  668.             for (;  cw && (cp[cw-1]==' ' || cp[cw-1]=='\t');  --cw)
  669.             ;
  670.         for (;;) {
  671.             afputc('\n', out);
  672.             awrite(cp, cw, out);
  673.             if (!ls)
  674.             break;
  675.             --ls;
  676.             c = *sp++;
  677.             if (c != '\n') {
  678.             awrite(cp+cw, cs-cw, out);
  679.             do {
  680.                 afputc(c,out);
  681.                 if (!ls)
  682.                 break;
  683.                 --ls;
  684.                 c = *sp++;
  685.             } while (c != '\n');
  686.             }
  687.         }
  688.     }
  689. }
  690.  
  691.  
  692.     FILE *
  693. rcswriteopen(const char *RCSname)
  694. /*
  695.  * Create the lock file corresponding to RCSNAME.
  696.  * Then try to open RCSNAME for reading and yield its FILE* descriptor.
  697.  * If all goes well, discard any previously acquired locks,
  698.  * and set frewrite to the FILE* descriptor of the lock file,
  699.  * which will eventually turn into the new RCS file.
  700.  */
  701. {
  702.     register char *tp;
  703.     register const char *sp, *lp;
  704.     FILE *f;
  705.     int fdesc, r;
  706.     struct buf *dirt;
  707.  
  708.     sp = RCSname;
  709.     dirt = &dirtfname[frewrite != NULL];
  710.     bufalloc(dirt, strlen(sp)+1);
  711.     tp = dirt->string;
  712.     if ((lp = strrchr(sp,SLASH)))
  713.         while (sp<=lp)
  714.             *tp++ = *sp++;
  715.     *tp++ = RCSSEP ? RCSSEP : RCSSUF;
  716. #if RCSSEP
  717.     /* Insert `,' and append file name.  */
  718.     lp = strrchr(sp, RCSSEP);
  719. #else
  720.     /* The file system doesn't allow `,'; use `v' as a poor substitute.  */
  721.     lp = sp + strlen(sp) - 2;
  722. #endif
  723.     while (sp<=lp)
  724.         *tp++ = *sp++;
  725.     *tp = '\0'; /* same length as RCSname */
  726.     tp = dirt->string;
  727.  
  728.     f = NULL;
  729.  
  730.     seteid();
  731.     ignoreints();
  732. #    if !open_can_creat
  733. #        define create(f,m) creat(f, m)
  734. #    else
  735. #        define create(f,m) open(f, O_CREAT|O_WRONLY|o_excltrunc, m)
  736. #        ifdef O_EXCL
  737. #            define o_excltrunc O_EXCL
  738. #        else
  739. #            define o_excltrunc O_TRUNC
  740. #        endif
  741. #    endif
  742.     if (0 <= (fdesc = create(tp, S_IRUSR|S_IRGRP|S_IROTH))) {
  743.         dirtfmaker[0] = effective;
  744.         errno = 0;
  745.         f = fopen(RCSname,"r");
  746.         if (frewrite)
  747.             /* We already have a lock somewhere else.  */
  748.             if (f) {
  749.             /* Discard the first acquired lock.  */
  750.             ffclose(frewrite);  frewrite = NULL;
  751.             if (unlink(newRCSfilename) < 0) {
  752.                 setrid();
  753.                 efaterror(newRCSfilename);
  754.             }
  755.             bufscpy(&dirtfname[0], tp);
  756.             } else {
  757.             /* Prefer the first acquired lock to this one.  */
  758.             r = close(fdesc)<0 || unlink(tp)<0;
  759.             restoreints();
  760.             setrid();
  761.             if (r)
  762.                 efaterror(tp);
  763.             return f;
  764.             }
  765.         if (!(frewrite = fdopen(fdesc,"w"))) {
  766.             setrid();
  767.             efaterror(newRCSfilename);
  768.         }
  769.     }
  770. #if !open_can_creat | !defined(O_EXCL)
  771.     else if (errno != ENOENT) {
  772.         /* Set errno=EEXIST if the RCS file is busy.  */
  773.         struct stat statbuf;
  774.         int old_errno = errno;
  775.         errno  =  stat(tp,&statbuf)==0 ? EEXIST : old_errno;
  776.     }
  777. #endif
  778.  
  779.     restoreints();
  780.     setrid();
  781.     return f;
  782. }
  783.  
  784.     void
  785. keepdirtemp(const char *name)
  786. /* Do not unlink name, either because it's not there any more,
  787.  * or because it has already been unlinked.
  788.  */
  789. {
  790.     register int i;
  791.     for (i=DIRTEMPNAMES; 0<=--i; )
  792.         if (dirtfname[i].string == name) {
  793.             dirtfmaker[i] = notmade;
  794.             return;
  795.         }
  796.     faterror("keepdirtemp");
  797. }
  798.  
  799.     const char *
  800. makedirtemp(register const char *name, int n)
  801. /*
  802.  * Have maketemp() do all the work if name==tmp.
  803.  * Otherwise, create a unique filename in name's dir using n and name
  804.  * and store it into the dirtfname[n].
  805.  * Because of storage in tfnames, dirtempunlink() can unlink the file later.
  806.  * Return a pointer to the filename created.
  807.  */
  808. {
  809.     register char *tp;
  810.     register const char *lastslash, *np;
  811.  
  812.     if (name == tmp())
  813.         return maketemp(n);
  814.     bufalloc(&dirtfname[n], strlen(name)+3);
  815.     np = tp = dirtfname[n].string;
  816.     if ((lastslash = strrchr(name,SLASH)))
  817.         while (name<=lastslash)
  818.             *tp++ = *name++;
  819.     *tp++ = RCSSEP ? RCSSEP : RCSSUF;
  820.     *tp++ = 'A'+n;
  821.     while ((*tp++ = *name++))
  822.         ;
  823.     dirtfmaker[n] = real;
  824.     return np;
  825. }
  826.  
  827.     void
  828. dirtempunlink()
  829. /* Clean up makedirtemp() files.  May be invoked by signal handler. */
  830. {
  831.     register int i;
  832.     enum maker m;
  833.  
  834.     for (i = DIRTEMPNAMES;  0 <= --i;  )
  835.         if ((m = dirtfmaker[i]) != notmade) {
  836.         if (m == effective)
  837.             seteid();
  838.         VOID unlink(dirtfname[i].string);
  839.         if (m == effective)
  840.             setrid();
  841.         dirtfmaker[i] = notmade;
  842.         }
  843. }
  844.